package operator

import (
	"context"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset"
	"gitverse.ru/synapse/kubelatte/pkg/util"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"math/rand"
	"os"
	ctrl "sigs.k8s.io/controller-runtime"
	"strconv"
	"sync"
	"time"
)

var (
	Mux sync.Mutex

	OperatorNamespace               = os.Getenv("NAMESPACE")
	LocalNamespaceMode              = os.Getenv("LOCAL_NAMESPACE_MODE")
	WithoutCert                     = os.Getenv("NO_CERT")
	CreationMode                    = os.Getenv("KBLT_CREATION_MODE")
	KbltCreatorLabelKey             = os.Getenv("KBLT_CREATOR_LABEL_KEY")
	KbltCreatorLabelValue           = os.Getenv("KBLT_CREATOR_LABEL_VALUE")
	KbltCreator                     = os.Getenv("KBLT_CREATOR")
	KbltMutator                     = os.Getenv("KBLT_MUTATOR")
	KbltValidator                   = os.Getenv("KBLT_VALIDATOR")
	NamespaceSelectorTI             = os.Getenv("KBLT_NAMESPACE_SLR_TI")
	NamespaceSelectorCommonResource = os.Getenv("KBLT_NAMESPACE_SLR_CMN")

	KbltSideEffect                            = os.Getenv("KBLT_SIDE_EFFECT")
	SideEffectRecreation                      = os.Getenv("KBLT_SIDE_EFFECT_RECREATION")
	OwnerRefSideEffect                        = os.Getenv("OWNER_REF_SIDE_EFFECT")
	timeoutSideEffectParentPublicationWaiting = os.Getenv("TIMEOUT_SECONDS_SIDE_EFFECT_PARENT_PUBLISHING_WAITING")
	TimeoutParentWaiting                      time.Duration

	EventOperation   eventsType
	Cl               clientset.V1Alpha1Interface
	IsInit           = IsInitSync{Mu: sync.Mutex{}}
	NSSelectorTI     *util.NamespaceSelector
	NSSelectorCommon *util.NamespaceSelector
)

type IsInitSync struct {
	IsInit bool
	Mu     sync.Mutex
}

var (
	PmTemplatesTotal = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "kblt_templates_total",
		Help: "The total number of processed templates",
	})
	PmTriggersTotal = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "kblt_triggers_total",
		Help: "The total number of processed triggers",
	})
)

type eventsType string

const (
	AddEvent    eventsType = "added"
	UpdateEvent eventsType = "updated"
	DeleteEvent eventsType = "removed"
)

const (
	EnvBoolValueTrue               = "true"
	EnvBoolValueFalse              = "false"
	LeaseLockName                  = "leader-operator"
	FactoryCreateRequestEvent      = "factory_create_request_event"
	WatcherUpdateReSyncPeriodEvent = "WatcherUpdateReSyncPeriodEvent"
	WatcherAddNewObjectEvent       = "WatcherAddNewObjectEvent"
	WatcherRemoveObjectEvent       = "WatcherRemoveObjectEvent"
	FactoryResourceRemoved         = "REMOVED"
	FactoryResourceCreateFailed    = "Failed"
	StorageUpdateTemplateEvent     = "StorageUpdateTemplateEvent"
	StorageUpdateTriggerEvent      = "StorageUpdateTriggerEvent"
	StorageUpdateScopeEvent        = "StorageUpdateScopeEvent"
	StorageUpdateNamespaceEvent    = "StorageUpdateNamespaceEvent"
	StorageDeleteTemplateEvent     = "StorageRemoveTemplateEvent"
	StorageDeleteTriggerEvent      = "StorageRemoveTriggerEvent"
	StorageDeleteScopeEvent        = "StorageRemoveScopeEvent"
	StorageDeleteNamespaceEvent    = "StorageRemoveNamespaceEvent"
	SyncAddNewObjectEvent          = "SyncAddNewObjectEvent"
)

type ReconcilerInterface interface {
	Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
	SetupWithManager(mgr ctrl.Manager) error
	AddOrUpdateEventHndl(ctx context.Context, req ctrl.Request) (err error)
	HealthCheck() bool
}

type Status struct {
	TypeConfig string                   `yaml:"typeConfig"`
	NameConfig string                   `yaml:"nameConfig"`
	Status     v1alpha1.ConditionStatus `yaml:"status"`
	Message    string                   `yaml:"message"`
}

func PrometheusInit() {
	prometheus.MustRegister(PmTemplatesTotal)
	prometheus.MustRegister(PmTriggersTotal)
}

func CheckHashInStatus(obj []v1alpha1.Condition) bool {
	for _, condition := range obj {
		if condition.Type == v1alpha1.LastHandledHash {
			return true
		}
	}
	return false
}

func AddHashCondition(data []byte) v1alpha1.Condition {
	return v1alpha1.Condition{
		Type:               v1alpha1.LastHandledHash,
		Status:             v1alpha1.ConditionTrue,
		LastTransitionTime: metav1.Now(),
		Message:            GetHash(data),
	}
}

func GetHash(data []byte) string {
	h := sha1.New()
	h.Write(data)
	sha1Hash := hex.EncodeToString(h.Sum(nil))
	return sha1Hash
}

func GetShortHash(message string) string {
	return GetHash([]byte(message))[0:10]
}

func GetRandomPart() string {
	return fmt.Sprintf("%d", rand.Int())
}

func IsCreatorRole() bool {
	return KbltCreator == "true"
}

func IsMutatorRole() bool {
	return KbltMutator == "true"
}

func IsValidatorRole() bool {
	return KbltValidator == "true"
}

func IsFullRole() bool {
	if IsCreatorRole() && IsMutatorRole() && IsValidatorRole() {
		return true
	}
	return false
}

func IsControllerNeeded(name string) bool {
	switch name {
	case "scope":
		return IsFullRole() || IsMutatorRole() || IsValidatorRole()
	case "trigger":
		return IsFullRole() || IsMutatorRole() || IsCreatorRole()
	case "template":
		return IsFullRole() || IsMutatorRole() || IsCreatorRole() || IsValidatorRole()
	case "triggerInstance":
		return IsFullRole() || IsCreatorRole()
	}
	return false
}

func InitEnv() {

	if NamespaceSelectorTI == "" {
		NamespaceSelectorTI = "{\"labelSelector\": {\"matchExpressions\": [{\"key\":\"kubelatte-injection\",\"operator\": \"In\",\"values\": [\"enabled\"]}]}}"
	}

	if NamespaceSelectorTI != "" {
		logs.Debugf("Namespace selector for TI is SET %s", NamespaceSelectorTI)
		selector := util.NamespaceSelector{}
		err := json.Unmarshal([]byte(NamespaceSelectorTI), &selector)
		if err != nil {
			logs.Errorf("Parse namespace selector for TI error %s", err.Error())
		}
		NSSelectorTI = &selector
	}

	if NamespaceSelectorCommonResource != "" {
		logs.Debugf("Namespace selector for Common is SET %s", NamespaceSelectorCommonResource)
		selector := util.NamespaceSelector{}
		err := json.Unmarshal([]byte(NamespaceSelectorCommonResource), &selector)
		if err != nil {
			logs.Errorf("Parse namespace selector for Common error %s", err.Error())
		}
		NSSelectorCommon = &selector
	}

	if KbltCreatorLabelKey == "" {
		KbltCreatorLabelKey = "kblt.creation.resource"
	}
	if KbltCreatorLabelValue == "" {
		KbltCreatorLabelValue = "true"
	}

	if timeoutSideEffectParentPublicationWaiting == "" {
		TimeoutParentWaiting = 5 * time.Second
	} else {
		timeout, err := strconv.Atoi(timeoutSideEffectParentPublicationWaiting)
		if err != nil {
			logs.Errorf("Parse timeoutSideEffectParentPublicationWaiting error %s", err.Error())
			return
		}
		TimeoutParentWaiting = time.Duration(timeout) * time.Second
	}

}

func IsCreationInClusterMode() bool {
	return CreationMode == "cluster"
}

func IsOperatorInClusterMode() bool {
	return LocalNamespaceMode == EnvBoolValueFalse
}

func GetOwnerRef(obj map[string]interface{}) (*metav1.OwnerReference, error) {
	metadata, ok := obj["metadata"]
	if !ok {
		return nil, fmt.Errorf("GetOwnerRef failed. Metadata not found")
	}

	name, ok := metadata.(map[string]interface{})["name"]
	if !ok {
		return nil, fmt.Errorf("GetOwnerRef failed. Name not found")
	}

	uid, ok := metadata.(map[string]interface{})["uid"]
	if !ok {
		return nil, fmt.Errorf("GetOwnerRef failed. UID not found")
	}

	apiVersion, ok := obj["apiVersion"]
	if !ok {
		return nil, fmt.Errorf("GetOwnerRef failed. ApiVersion not found")
	}

	kind, ok := obj["kind"]
	if !ok {
		return nil, fmt.Errorf("GetOwnerRef failed. Kind not found")
	}

	return &metav1.OwnerReference{
		APIVersion: apiVersion.(string),
		Kind:       kind.(string),
		Name:       name.(string),
		UID:        types.UID(uid.(string)),
	}, nil
}

func RemoveCondition(conditions []v1alpha1.Condition, ct v1alpha1.ConditionType) []v1alpha1.Condition {
	for i, cond := range conditions {
		if cond.Type == ct {
			return append(conditions[:i], conditions[i+1:]...)
		}
	}
	return conditions
}
